package me.zhengjie.gas.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import me.zhengjie.domain.Dept;
import me.zhengjie.gas.dto.CardBindDto;
import me.zhengjie.gas.dto.TransactionDataDto;
import me.zhengjie.gas.model.ResponseDto;
import me.zhengjie.gas.mqtt.MqttTemplate;
import me.zhengjie.gas.repositories.CardBindRepository;
import me.zhengjie.gas.repositories.TransactionRepository;
import me.zhengjie.gas.utils.CamelCaseRowMapper;

import me.zhengjie.dto.DeptDto;
import me.zhengjie.dto.UserDto;
import me.zhengjie.service.DeptService;
import me.zhengjie.service.UserService;
import me.zhengjie.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
public class TransactionService {

	@Autowired
	private TransactionRepository transactionRepository;

	@Autowired
	private CardBindRepository cardBindRepository;

	@Autowired
	private UserService userService;

	@Autowired
	private DeptService deptService;

	@Autowired
	private NamedParameterJdbcTemplate jdbcTemplate;


	public ResponseDto newTransaction(TransactionDataDto data) {
		if (transactionRepository.findByUuidStr(data.getUuidStr()) != null) {
			return new ResponseDto(2, "交易已存在");
		}

		Long userId = SecurityUtils.getCurrentUserId();
		UserDto user = userService.findById(userId);

		if (data.getDeptId() == null) {
			data.setDeptId(user.getDept().getId());
		}

		DeptDto deptDto = deptService.findById(user.getDept().getId());
		if (deptDto == null) {
			return new ResponseDto(1, "无效的数据");
		}

		try {
			data.setId(null);
			transactionRepository.save(data);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		handleTransaction(data);

		ObjectMapper objectMapper = new ObjectMapper();
		objectMapper.registerModule(new JavaTimeModule());
		try {
			String jsonText = objectMapper.writeValueAsString(data);
			MqttTemplate.publish("/GAS/" + deptDto.getId() + "/transaction", jsonText);
		} catch (JsonProcessingException e) {
			throw new RuntimeException(e);
		}

		return new ResponseDto(0, "success", data.getId());
	}

	public void handleTransaction(TransactionDataDto dto) {
		// 查询油枪对应的油罐
		String sql = "select t.tank_uuid, t.volume from eladmin.line l \n" +
				"inner join eladmin.tank t on t.tank_uuid = l.tank_uuid \n" +
				"where t.dept_id = :deptId and l.line_id = :lineId";
		MapSqlParameterSource params = new MapSqlParameterSource();
		params.addValue("deptId", dto.getDeptId());
		params.addValue("lineId", dto.getLineId());
		List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, params);
		if (CollectionUtils.isEmpty(list)) {
			return;
		}

		BigDecimal orgVolume = (BigDecimal) list.get(0).get("volume");
		if (orgVolume == null) {
			return;
		}

		double volume = orgVolume.doubleValue();
		volume -= dto.getVolume();

		// 修改油罐余额
		sql = "update eladmin.tank set \"volume\"=:volume where tank_uuid=:tankUuid";
		params = new MapSqlParameterSource();
		params.addValue("volume", volume);
		params.addValue("tankUuid", list.get(0).get("tank_uuid"));
		jdbcTemplate.update(sql, params);
	}

	public ResponseDto listAllUuid(Long deptId, LocalDateTime startDate, LocalDateTime endDate) {
		MapSqlParameterSource params = new MapSqlParameterSource();
		params.addValue("deptId", deptId);

		String sql;
		if (startDate == null || endDate == null) {
			sql = "select td.trans_uuid from eladmin.transaction_data td " +
					"where td.dept_id=:deptId";
		} else {
			sql = "select td.trans_uuid from eladmin.transaction_data td " +
					"where td.dept_id=:deptId and td.created_ts>=:startDate and td.created_ts<=:endDate";
			params.addValue("startDate", startDate);
			params.addValue("endDate", endDate);
		}

		List<Map<String, Object>> list = jdbcTemplate.query(sql, params, new CamelCaseRowMapper());
		return new ResponseDto(0, "success", list);
	}

	public ResponseDto listAll(LocalDateTime startDate, LocalDateTime endDate, Long deptId, Integer lineNo) {
		Long userId = SecurityUtils.getCurrentUserId();
		UserDto user = userService.findById(userId);

		List<Dept> deptList = deptService.findByPid(user.getDept().getId());
		// 只取deptId
		List<Long> deptIds = deptList.stream().map(Dept::getId).collect(Collectors.toList());

		List<TransactionDataDto> list;
		if (deptId == null || deptId == 0) {
			list = transactionRepository.findAllByDeptIds(startDate, endDate, deptIds);
		} else {
			list = transactionRepository.findAllByDeptId(startDate, endDate, deptId == 0 ? null : deptId, lineNo == 0 ? null : lineNo);
		}

		return new ResponseDto(0, "success", list);
	}

	public ResponseDto deptAll(int pageSize, int pageIndex) {
		Long userId = SecurityUtils.getCurrentUserId();
		UserDto user = userService.findById(userId);

		try {
			List<TransactionDataDto> result = transactionRepository.findLastByDeptId(user.getDept().getId(), pageSize, pageIndex * pageSize);
			return new ResponseDto(0, "success", result);
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new RuntimeException(ex);
		}
	}

	public ResponseDto findTransactionByLicenseNo(String licenseNo) {
		Long userId = SecurityUtils.getCurrentUserId();
		UserDto user = userService.findById(userId);

		List<Dept> deptList = deptService.findByPid(user.getDept().getId());
		// 只取deptId
		List<Long> deptIds = deptList.stream().map(Dept::getId).collect(Collectors.toList());

		CardBindDto dto = cardBindRepository.findByLicenseNo(deptIds, licenseNo);
		if (dto == null) {
			return new ResponseDto(1, "Not found");
		}

		String logicCardNo = dto.getCardNo().substring(0, 8);
		String physicalCardNo = dto.getCardNo().substring(8);

		List<TransactionDataDto> result = transactionRepository.findLastByCardNo(deptIds, logicCardNo, physicalCardNo);
		return new ResponseDto(0, "success", result);
	}
}
